﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace AGrandVenture
{
    public class PlayerCharacter
    {
        #region Enums
        public enum RaceType { Human, HighElf, DarkElf, Dwarf, Gnome, HalfGiant, 
                                Orc, Troll, Angel, Demon, Goblin, Dragon, WereBeast,
                                Fey, Hybrid, Insectoid, Undead }
        public enum ClassType {Assassin, Barbarian, Bard, Brawler, Cleric, Conjurer, Druid,
                                Duelist, Elementalist, Gladiator, Huntsman, Illusionist,
                                Knight, Magi, MartialArtist, Monk, Necromancer, Paladin,
                                Priest, Psionicist, Shaman, Smuggler, Technomage, Thief,
                                Warlock, Witchdoctor}
        public enum CharacterStatus { Normal, Poisoned, Slowed, Snared, 
                                      Frozen, Burned, Stunned, Cursed, Diseased,
                                      Charmed, Choking, Burning, Phased,
                                      Possessed, Haunted, Shrunken, Grown,
                                      Lucky, Unlucky, Crippled, Bleeding, Incapacitated,
                                      Drowning, Blessed, Decapitated }
        public enum GenderType { Male, Female, Unknown }
        #endregion

        #region StatDeclarations

        //Informational
        private int characterLevel = 1;
        private string characterName = "";
        private GenderType characterGender;
        private ClassType characterClass;
        private RaceType characterRace;
        private string characterTitle = "";
        private float characterAlignment;  //0 = evil, .5 = neutral, 1 = good
        
        //Inherent, base stats
        private int baseStrength,
                    basePerception,
                    baseAgility,
                    baseConstitution,
                    baseCharisma,
                    baseIntelligence,
                    baseLuck,
                    baseHP,
                    baseEnergy;

        //Stored pre-calculated bonus stats from all equipment/skills/race bonuses
        private int bonusStrength,
                    bonusPerception,
                    bonusAgility,
                    bonusConstitution,
                    bonusCharisma,
                    bonusIntelligence,
                    bonusLuck,
                    bonusHP,
                    bonusEnergy;

        //Rating stats calculated from total stats + equipment/skill/race bonus
        private int rstatArmorClass,
                    rstatPhysicalAttack,
                    rstatMagicalAttack,
                    rstatCriticalStrike,
                    rstatMentalResistance,
                    rstatArcaneResistance,
                    rstatDiseaseResistance,
                    rstatPoisonResistance,
                    rstatFireResistance,
                    rstatIceResistence;
        

        //Current status / effects
        private int remainingHitPoints,
                    remainingEnergy;
        private CharacterStatus currentStatus;

        //Inventory items
        private List<InventoryItem> InventoryItems = new List<InventoryItem>();
        private List<InventoryItem> EquipmentItems = new List<InventoryItem>();
        private static int maxInventorySize = 30;

#endregion

        #region Accessors
        public List<InventoryItem> InventoryList
        {
            get { return InventoryItems; }
        }

        public List<InventoryItem> EquippedList
        {
            get { return EquipmentItems; }
        }

        public string Alignment
        {
            get
            {
                if (characterAlignment < 0.1f)
                    return "Chaotic Evil";
                else if (characterAlignment < 0.2f)
                    return "Neutral Evil";
                else if (characterAlignment < 0.3f)
                    return "Lawful Evil";
                else if (characterAlignment <= 0.4f)
                    return "Chaotic Neutral";
                else if (characterAlignment < 0.6f)
                    return "Neutral";
                else if (characterAlignment < 0.7f)
                    return "Lawful Neutral";
                else if (characterAlignment < 0.8f)
                    return "Chaotic Good";
                else if (characterAlignment < 0.9f)
                    return "Neutral Good";
                else 
                    return "Lawful Good";
            }
        }

        //Base inherent stats
        public int Level
        {
            get { return characterLevel; }
            set { characterLevel = value; }
        }
        public string Name
        {
            get { return characterName; }
            set {characterName = value; }
        }
        public string Title
        {
            get { return characterTitle; }
            set { characterTitle = value; }
        }
        public ClassType Class
        {
            get { return characterClass; }
            set {characterClass = value; }
        }
        public GenderType Gender
        {
            get { return characterGender; }
            set { characterGender = value; }
        }
        public RaceType Race
        {
            get { return characterRace; }
            set { characterRace = value; }
        }
        public int BaseStrength
        {
            get { return baseStrength; }
            set {baseStrength = value; }
        }
        public int BasePerception
        {
            get { return basePerception; }
            set {basePerception = value; }
        }
        public int BaseAgility
        {
            get { return baseAgility; }
            set {baseAgility = value; }
        }
        public int BaseConstitution
        {
            get { return baseConstitution; }
            set {baseConstitution = value; }
        }
        public int BaseCharisma
        {
            get { return baseCharisma; }
            set {baseCharisma = value; }
        }
        public int BaseIntelligence
        {
            get { return baseIntelligence; }
            set {baseIntelligence = value; }
        }
        public int BaseLuck
        {
            get { return baseLuck; }
            set {baseLuck = value; }
        }
        public int BaseHP
        {
            get { return baseHP; }
            set { baseHP = value; }
        }
        public int BaseEnergy
        {
            get { return baseEnergy; }
            set { baseEnergy = value; }
        }

        //Calculated bonuses
        public int BonusStrength
        {
            get { return bonusStrength; }
            set { bonusStrength = value; }
        }
        public int BonusPerception
        {
            get { return bonusPerception; }
            set { bonusPerception = value; }
        }
        public int BonusAgility
        {
            get { return bonusAgility; }
            set { bonusAgility = value; }
        }
        public int BonusConstitution
        {
            get { return bonusConstitution; }
            set { bonusConstitution = value; }
        }
        public int BonusCharisma
        {
            get { return bonusCharisma; }
            set { bonusCharisma = value; }
        }
        public int BonusIntelligence
        {
            get { return bonusIntelligence; }
            set { bonusIntelligence = value; }
        }
        public int BonusLuck
        {
            get { return bonusLuck; }
            set { bonusLuck = value; }
        }
        public int BonusHP
        {
            get { return bonusHP; }
            set { bonusHP = value; }
        }
        public int BonusEnergy
        {
            get { return bonusEnergy; }
            set { bonusEnergy = value; }
        }

        //Calculated total stats
        public int TotalStrength
        {
            get { return baseStrength + bonusStrength; }
        }
        public int TotalPerception
        {
            get { return basePerception + bonusPerception; }
        }
        public int TotalAgility
        {
            get { return baseAgility + bonusAgility; }
        }
        public int TotalConstitution
        {
            get { return baseConstitution + bonusConstitution; }
        }
        public int TotalCharisma
        {
            get { return baseCharisma + bonusCharisma; }
        }
        public int TotalIntelligence
        {
            get { return baseIntelligence + bonusIntelligence; }
        }
        public int TotalLuck
        {
            get { return baseLuck + bonusLuck; }
        }
        public int TotalHP
        {
            get { return baseHP+ bonusHP; }
        }
        public int TotalEnergy
        {
            get { return baseEnergy + bonusEnergy; }
        }

        //Rating stats
        public int ArmorClass
        {
            get { return rstatArmorClass; }
            set {rstatArmorClass = value; }
        }
        public int PhysicalAttackRating
        {
            get { return rstatPhysicalAttack; }
            set {rstatPhysicalAttack = value; }
        }
        public int MagicalAttackRating
        {
            get { return rstatMagicalAttack; }
            set {rstatMagicalAttack = value; }
        }
        public int CriticalStrikeRating
        {
            get { return rstatCriticalStrike; }
            set { rstatCriticalStrike = value; }
        }
        public int MentalResistance
        {
            get { return rstatMentalResistance; }
            set {rstatMentalResistance = value; }
        }
        public int ArcaneResistance
        {
            get { return rstatArcaneResistance; }
            set {rstatArcaneResistance = value; }
        }
        public int PoisonResistance
        {
            get { return rstatPoisonResistance; }
            set {rstatPoisonResistance = value; }
        }
        public int DiseaseResistance
        {
            get { return rstatDiseaseResistance; }
            set { rstatDiseaseResistance = value; }
        }
        public int FireResistance
        {
            get { return rstatFireResistance; }
            set {rstatFireResistance = value; }
        }
        public int IceResistance
        {
            get { return rstatIceResistence; }
            set {rstatIceResistence = value; }
        }

        //Status
        public int RemainingHP
        {
            get { return remainingHitPoints; }
            set { remainingHitPoints = value; }
        }
        public int RemainingEnergy
        {
            get { return remainingEnergy; }
            set { remainingEnergy = value; }
        }
        public CharacterStatus Status
        {
            get { return currentStatus; }
            set { currentStatus = value; }
        }

#endregion

        #region Constructor
        public PlayerCharacter(string name, ClassType ct, RaceType race)
        {
            Name = name;
            Title = "Adventurer";
            Class = ct;
            characterRace = race;
            characterAlignment = 0.5f;
            setBaseStats(race);
        }

        private void setBaseStats(RaceType race)
        {
            switch (race)
            {
                case RaceType.Human:
                    baseStrength = 10;
                    basePerception = 10;
                    baseAgility = 10;
                    baseConstitution = 12;
                    baseCharisma = 10;
                    baseIntelligence = 12;
                    baseLuck = 12;
                    break;
                case RaceType.HighElf:
                    baseStrength = 8;
                    basePerception = 12;
                    baseAgility = 10;
                    baseConstitution = 8;
                    baseCharisma = 12;
                    baseIntelligence = 14   ;
                    baseLuck = 8;
                    break;
                case RaceType.DarkElf:
                    baseStrength = 10;
                    basePerception = 12;
                    baseAgility = 14;
                    baseConstitution = 8;
                    baseCharisma = 8;
                    baseIntelligence = 14;
                    baseLuck = 6;
                    break;
                case RaceType.Dwarf:
                    baseStrength = 14;
                    basePerception = 8;
                    baseAgility = 10;
                    baseConstitution = 14;
                    baseCharisma = 8;
                    baseIntelligence = 10;
                    baseLuck = 8;
                    break;
                case RaceType.Gnome:
                    baseStrength = 8;
                    basePerception = 14;
                    baseAgility = 14;
                    baseConstitution = 8;
                    baseCharisma = 8;
                    baseIntelligence = 16;
                    baseLuck = 4;
                    break;
                case RaceType.HalfGiant:
                    baseStrength = 16;
                    basePerception = 12;
                    baseAgility = 8;
                    baseConstitution = 16;
                    baseCharisma = 8;
                    baseIntelligence = 8;
                    baseLuck = 8;
                    break;
                case RaceType.Orc:
                    baseStrength = 20;
                    basePerception = 12;
                    baseAgility = 12;
                    baseConstitution = 14;
                    baseCharisma = 6;
                    baseIntelligence = 6;
                    baseLuck = 8;
                    break;
                case RaceType.Troll:
                    baseStrength = 18;
                    basePerception = 8;
                    baseAgility = 8;
                    baseConstitution = 20;
                    baseCharisma = 4;
                    baseIntelligence = 12;
                    baseLuck = 8;
                    break;
                case RaceType.Angel:
                    baseStrength = 14;
                    basePerception = 12;
                    baseAgility = 12;
                    baseConstitution = 18;
                    baseCharisma = 12;
                    baseIntelligence = 10;
                    baseLuck = 0;
                    break;
                case RaceType.Demon:
                    baseStrength = 16;
                    basePerception = 16;
                    baseAgility = 16;
                    baseConstitution = 14;
                    baseCharisma = 4;
                    baseIntelligence = 10;
                    baseLuck = 0;
                    break;
                case RaceType.Goblin:
                    baseStrength = 12;
                    basePerception = 18;
                    baseAgility = 18;
                    baseConstitution = 10;
                    baseCharisma = 2;
                    baseIntelligence = 8;
                    baseLuck = 12;
                    break;
                case RaceType.Dragon:
                    baseStrength = 8;
                    basePerception = 8;
                    baseAgility = 8;
                    baseConstitution = 10;
                    baseCharisma = 10;
                    baseIntelligence = 18;
                    baseLuck = 4;
                    break;
                case RaceType.WereBeast:
                    baseStrength = 16;
                    basePerception = 16;
                    baseAgility = 12;
                    baseConstitution = 16;
                    baseCharisma = 2;
                    baseIntelligence = 10;
                    baseLuck = 6;
                    break;
                case RaceType.Fey:
                    baseStrength = 6;
                    basePerception = 10;
                    baseAgility = 10;
                    baseConstitution = 8;
                    baseCharisma = 16;
                    baseIntelligence = 12;
                    baseLuck = 12;
                    break;
                case RaceType.Hybrid:
                    baseStrength = 18;
                    basePerception = 12;
                    baseAgility = 12;
                    baseConstitution = 14;
                    baseCharisma = 4;
                    baseIntelligence = 12;
                    baseLuck = 2;
                    break;
                case RaceType.Insectoid:
                    baseStrength = 16;
                    basePerception = 16;
                    baseAgility = 14;
                    baseConstitution = 16;
                    baseCharisma = 2;
                    baseIntelligence = 8;
                    baseLuck = 6;
                    break;
                case RaceType.Undead:
                    baseStrength = 18;
                    basePerception = 18;
                    baseAgility = 18;
                    baseConstitution = 18;
                    baseCharisma = 1;
                    baseIntelligence = 5;
                    baseLuck = 2;
                    break;
            }
        }
        #endregion

        #region PlayerActions

        /// <summary>
        /// Accepts an onscreen InventoryItem to be added to player Inventory
        /// </summary>
        /// <param name="item">Item to be accepted</param>
        /// <returns>True if item was added to inventory</returns>
        public Boolean addToInventory(InventoryItem item)
        {
            if (InventoryItems.Count < maxInventorySize)
            {
                InventoryItems.Add(item);
                if (!Player.Character.isEquipSlotOccupied(item.EquipSlot))
                    equipItem(item);
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Attempts to equip the specified item
        /// </summary>
        /// <param name="item">Item to be equipped by player</param>
        /// <returns>True if equip was successful, otherwise false</returns>
        public Boolean equipItem(InventoryItem item)
        {
            if (item.EquipSlot == InventoryItem.EquipmentSlot.None)
                return false;

            //Nothing currently equipped
            if (!isEquipSlotOccupied(item.EquipSlot))
            {
                EquipmentItems.Add(item);
                item.IsEquipped = true;
                InventoryItems.Remove(item);
                return true;
            }
            else
            {//Try to replace existing equipment
                if (unequipSlot(item.EquipSlot))
                {
                    EquipmentItems.Add(item);
                    item.IsEquipped = true;
                    InventoryItems.Remove(item);
                    return true;
                }
            }
            //Equip failed
            return false;
        }

        /// <summary>
        /// Attempts to remove any equipped item from the specified slot
        /// </summary>
        /// <param name="slot">InventoryItem.EquipmentSlot from which to remove the item</param>
        /// <returns>True if unequip was successful, otherwise false</returns>
        public Boolean unequipSlot(InventoryItem.EquipmentSlot slot)
        {
            if (slot == InventoryItem.EquipmentSlot.None)
                return false;

            foreach (InventoryItem item in EquipmentItems)
            {
                if (item.EquipSlot == slot)
                {
                    if (item.IsCursed &&                              //Cannot unequip cursed items, 
                        slot != InventoryItem.EquipmentSlot.Finger && //but don't stop at first ring
                        slot != InventoryItem.EquipmentSlot.Trinket)  //or trinket
                        return false;
                    else if (!item.IsCursed)
                    {
                        EquipmentItems.Remove(item);
                        item.IsEquipped = false;
                        InventoryItems.Add(item);
                    }
                }
            }//Either no item was found, or all rings/trinkets found were cursed
            return false;
        }

        /// <summary>
        /// Accepts positive/negative float to alter player alignment by small increment (suggested between 0.01 and 0.2f)
        /// </summary>
        /// <param name="change">Amount to shift alignment</param>
        public void alterAlignment(float change)
        {
            characterAlignment += change;

            //Don't allow alignment outside of 0-1 range
            characterAlignment = (characterAlignment < 0.0f ? 0.0f : 
                                  characterAlignment > 1.0f ? 1.0f : 
                                  characterAlignment);
        }

        #endregion

        #region HelperMethods

        /// <summary>
        /// Check to see if an item is occupying the specified slot (or slots, in case of rings/trinkets)
        /// </summary>
        /// <param name="slot">InventoryItem.Equipment slot to be checked</param>
        /// <returns>True if slot is occupied (or both slots, in case of rings/trinkets), otherwise false</returns>
        public bool isEquipSlotOccupied(InventoryItem.EquipmentSlot slot)
        {
            if (slot == InventoryItem.EquipmentSlot.None)
                return true;

            int ringCount = 0,
                trinketCount = 0,
                earringCount = 0;

            foreach (InventoryItem item in EquipmentItems)
            {
                //Count rings, earrings and trinkets
                if (slot == InventoryItem.EquipmentSlot.Finger  &&
                     item.EquipSlot == slot)
                    ringCount++;
                else if (slot == InventoryItem.EquipmentSlot.Trinket  &&
                     item.EquipSlot == slot)
                    trinketCount++;
                else if (slot == InventoryItem.EquipmentSlot.Ear &&
                     item.EquipSlot == slot)
                    earringCount++;

                //Respond true if found
                else if (item.EquipSlot == slot)
                    return true;
            }

            if ((slot == InventoryItem.EquipmentSlot.Finger && ringCount >= 2) ||
                (slot == InventoryItem.EquipmentSlot.Ear && earringCount >= 2) ||
                (slot == InventoryItem.EquipmentSlot.Trinket && trinketCount >= 2))
                return true;
            else
                return false;
        }

        public InventoryItem getItemInSlot(InventoryItem.EquipmentSlot slot)
        {
            if (isEquipSlotOccupied(slot))
            {
                foreach (InventoryItem item in EquipmentItems)
                {
                    //Return item if found
                    if (item.EquipSlot == slot)
                        return item;
                }
            }

            //If above fails, prevent errors
            if (EquipmentItems.Count > 0)
                return EquipmentItems[0];
            else
                return null;
        }

        public string getBase(string stat)
        {
            //"STR", "AGI", "INT", "CON", "CHA", "PER", "LCK" };
            switch (stat)
            {
                case "STR": return BaseStrength.ToString();
                case "AGI": return BaseAgility.ToString();
                case "INT": return BaseIntelligence.ToString();
                case "CON": return BaseConstitution.ToString();
                case "CHA": return BaseCharisma.ToString();
                case "PER": return BasePerception.ToString();
                case "LCK": return BaseLuck.ToString();
                default: return "Not Implemented";
            }
        }
        public string getBonus(string stat)
        {
            //"STR", "AGI", "INT", "CON", "CHA", "PER", "LCK" };
            switch (stat)
            {
                case "STR": return BonusStrength.ToString();
                case "AGI": return BonusAgility.ToString();
                case "INT": return BonusIntelligence.ToString();
                case "CON": return BonusConstitution.ToString();
                case "CHA": return BonusCharisma.ToString();
                case "PER": return BonusPerception.ToString();
                case "LCK": return BonusLuck.ToString();
                default: return "Not Implemented";
            }
        }
        public string getTotal(string stat)
        {
            switch (stat)
            {
                case "STR": return TotalStrength.ToString(); 
                case "AGI": return TotalAgility.ToString();
                case "INT": return TotalIntelligence.ToString();
                case "CON": return TotalConstitution.ToString();
                case "CHA": return TotalCharisma.ToString();
                case "PER": return TotalPerception.ToString();
                case "LCK": return TotalLuck.ToString();
                case "Mental": return MentalResistance.ToString();
                case "Arcane": return ArcaneResistance.ToString();
                case "Poison": return PoisonResistance.ToString();
                case "Disease": return DiseaseResistance.ToString();
                case "Fire": return FireResistance.ToString();
                case "Cold": return IceResistance.ToString();
                case "Armor": return ArmorClass.ToString();
                case "Crit %": return CriticalStrikeRating.ToString();
                case "P. Attack": return PhysicalAttackRating.ToString();
                case "M. Attack": return MagicalAttackRating.ToString();
                default: return "Not Implemented";
            }
        }
        #endregion

    }
}
